import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { AbstractHttpAdapter, HttpAdapterHost } from '@nestjs/core';
import * as dotenv from 'dotenv';

dotenv.config();

@Catch()
export class AllExceptionFilter implements ExceptionFilter {
  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  private readonly logger: Logger = new Logger(AllExceptionFilter.name);

  catch(exception: Error, host: ArgumentsHost): void {
    const isDev = process.env.NODE_ENV === 'development';
    this.logger.error(exception);

    const httpAdapter = this.httpAdapterHost?.httpAdapter;
    if (httpAdapter && exception instanceof HttpException) {
      this.handleHttpException(exception as HttpException, host, httpAdapter);
    } else {
      if (isDev) {
        this.logger.error(exception.stack);
      } else {
        this.logger.error(exception.message);
      }
    }
  }

  private handleHttpException(
    exception: HttpException,
    host: ArgumentsHost,
    httpAdapter: AbstractHttpAdapter,
  ): void {
    const ctx = host.switchToHttp();
    const isDev = process.env.NODE_ENV === 'development';

    const httpStatus =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const responseBody = {
      statusCode: httpStatus,
      timestamp: new Date().toISOString(),
      path: httpAdapter.getRequestUrl(ctx.getRequest()),
      // 如果包含了换行符，说明是系统内部错误，不能暴露给客户端
      message: exception.message.includes('\n')
        ? 'Internal server error'
        : exception.message,
    };

    if (isDev) {
      this.logger.error(exception.stack);
    }

    httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
  }
}
